﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Logic.Global;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.SceneManagement;
using UnityEngine.U2D;
using Environment = Logic.Global.Environment;

namespace Core.Mgr._ResMgr
{
    public static class ResMgr
    {
        /// <summary>
        /// 异步加载资源
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key">加载路径</param>
        /// <param name="result">返回的结果</param>
        public static IEnumerator LoadAsync<T>(string key, T result)
        {
            bool isFinish = false;
            LoadAsync(key, delegate(T obj)
            {
                result = obj;
                isFinish = true;
            });
            yield return new WaitUntil(() => isFinish);
        }

        /// <summary>
        /// 异步加载资源
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key">加载路径</param>
        /// <param name="finishCb">完成回调</param>
        /// <param name="processCb">进度回调</param>
        /// <param name="failCb"></param>
        /// <returns></returns>
        public static void LoadAsync<T>(string key, Action<T> finishCb = null, Action<float> processCb = null,
            Action failCb = null)
        {
            GameRoot.Ins.StartCoroutine(LoadAssetAsync_(key, finishCb, processCb, failCb));
        }

        private static IEnumerator LoadAssetAsync_<T>(string key, Action<T> finishCb, Action<float> processCb,
            Action failCb)
        {
            AsyncOperationHandle<T> handle = Addressables.LoadAssetAsync<T>(key);
            while (!handle.IsDone)
            {
                processCb?.Invoke(handle.PercentComplete);
                yield return null;
            }

            if (handle.Status == AsyncOperationStatus.Succeeded)
                finishCb?.Invoke(handle.Result);
            else if (handle.Status == AsyncOperationStatus.Failed)
            {
                Debug.LogError(handle.OperationException.Data);
                failCb?.Invoke();
            }
        }

        /// <summary>
        /// 异步加载资源
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key">加载路径</param>
        /// <param name="finishCb">完成回调</param>
        /// <param name="processCb">进度回调</param>
        /// <returns></returns>
        public static void LoadAsync<T>(string key, Action<T, AsyncOperationHandle<T>> finishCb = null,
            Action<float> processCb = null, Action failCb = null)
        {
            GameRoot.Ins.StartCoroutine(LoadAssetAsync_(key, finishCb, processCb, failCb));
        }

        private static IEnumerator LoadAssetAsync_<T>(string key, Action<T, AsyncOperationHandle<T>> finishCb,
            Action<float> processCb, Action failCb)
        {
            AsyncOperationHandle<T> handle = Addressables.LoadAssetAsync<T>(key);
            while (!handle.IsDone)
            {
                processCb?.Invoke(handle.PercentComplete);
                yield return null;
            }

            if (handle.Status == AsyncOperationStatus.Succeeded)
                finishCb?.Invoke(handle.Result, handle);
            else if (handle.Status == AsyncOperationStatus.Failed)
            {
                Debug.LogError(handle.OperationException.Data);
                failCb?.Invoke();
            }
        }

        /// <summary>
        /// 异步加载场景
        /// </summary>
        public static void LoadSceneAsync(string key, Action<float> processCb = null, Action finishCb = null,
            Action failCb = null, LoadSceneMode sceneMode = LoadSceneMode.Single, bool activateOnLoad = true)
        {
            GameRoot.Ins.StartCoroutine(LoadSceneAsync_(key, processCb, finishCb, failCb, sceneMode, activateOnLoad));
        }

        private static IEnumerator LoadSceneAsync_(string key, Action<float> processCb = null, Action finishCb = null,
            Action failCb = null, LoadSceneMode sceneMode = LoadSceneMode.Single, bool activateOnLoad = true)
        {
            var handle = Addressables.LoadSceneAsync(key, sceneMode, activateOnLoad);
            while (!handle.IsDone)
            {
                processCb?.Invoke(handle.PercentComplete);
                yield return null;
            }

            if (handle.Status == AsyncOperationStatus.Succeeded)
                finishCb?.Invoke();
            else if (handle.Status == AsyncOperationStatus.Failed)
            {
                Debug.LogError(handle.OperationException.Data);
                failCb?.Invoke();
            }
        }

        /// <summary>
        /// 异步去加载所有资源
        /// </summary>
        public static void AsyncLoadAllAsset<T>(Dictionary<string, T> assetDic, Action<List<T>> finishCallback)
        {
            List<string> keyList = assetDic.Keys.ToList();
            int finishNum = 0;
            for (int i = 0; i < assetDic.Count; i++)
            {
                var i1 = i;
                LoadAsync(keyList[i], delegate(T obj)
                {
                    assetDic[keyList[i1]] = obj;
                    finishNum++;
                    if (finishNum == assetDic.Count)
                    {
                        finishCallback?.Invoke(assetDic.Values.ToList());
                    }
                });
            }
        }

        /// <summary>
        /// 异步实例化GameObject
        /// </summary>
        /// <param name="key"></param>
        /// <param name="parent"></param>
        /// <param name="finishCb"></param>
        /// <param name="processCb"></param>
        /// <returns></returns>
        public static void InstantiateAsync(object key, Transform parent = null, Action<GameObject> finishCb = null,
            Action<float> processCb = null)
        {
            GameRoot.Ins.StartCoroutine(_InstantiateAsync(key, parent, finishCb, processCb));
        }

        private static IEnumerator _InstantiateAsync(object key, Transform parent, Action<GameObject> finishCb,
            Action<float> processCb)
        {
            var handle = Addressables.InstantiateAsync(key, Vector3.zero, Quaternion.identity, parent, true);
            while (!handle.IsDone)
            {
                processCb?.Invoke(handle.PercentComplete);
                yield return null;
            }

            if (handle.Status == AsyncOperationStatus.Succeeded)
                finishCb?.Invoke(handle.Result);
        }

        /// <summary>
        /// 异步加载精灵图集中单张精灵
        /// </summary>
        /// <param name="atlasName"></param>
        /// <param name="key">精灵的名称（不带文件后缀）</param>
        /// <param name="finishCb"></param>
        /// <returns></returns>
        public static void LoadSprite(string atlasName, string key, Action<Sprite> finishCb = null)
        {
            GameRoot.Ins.StartCoroutine(LoadSprite_(atlasName, key, finishCb));
        }

        private static IEnumerator LoadSprite_(string atlasName, string key, Action<Sprite> finishCb = null)
        {
            return LoadAssetAsync_<SpriteAtlas>(atlasName, (atals) =>
            {
                var sprite = atals.GetSprite(key);
                finishCb?.Invoke(sprite);
            }, null, null);
        }

        /// <summary>
        /// 异步加载精灵图集表
        /// </summary>
        /// <param name="key"></param>
        /// <param name="finishCb"></param>
        /// <returns></returns>
        public static void LoadSpriteAtlasAsync(string key, Action<List<Sprite>> finishCb = null)
        {
            GameRoot.Ins.StartCoroutine(_LoadSpriteAtlasAsync(key, finishCb));
        }

        private static IEnumerator _LoadSpriteAtlasAsync(string key, Action<List<Sprite>> finishCb = null)
        {
            yield return LoadAssetAsync_<SpriteAtlas>(key, (atals) =>
            {
                Sprite[] sprites = new Sprite[atals.spriteCount];
                atals.GetSprites(sprites);
                finishCb?.Invoke(sprites.ToList());
            }, null, null);
        }

        public static bool ContinuesKey(string key)
        {
            var downloadSizeTask = Addressables.GetDownloadSizeAsync(key);
            downloadSizeTask.WaitForCompletion();
            return downloadSizeTask.Status == AsyncOperationStatus.Succeeded;
        }
    }
}